<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
    <title>ShellCheck: SC2012 – Use `find` instead of `ls` to better handle non-alphanumeric filenames.</title>
    <link rel="stylesheet" href="css/bootstrap.min.css" />
  </head>
  <body style="margin-left: auto; margin-right: auto; max-width: 800px">
    <h1>SC2012 – ShellCheck Wiki</h1>
    <a href="https://github.com/koalaman/shellcheck/wiki/SC2012">See this page on GitHub</a>
    <p style="display: none"><a href="index.html">Sitemap</a></p>
    <hr />
    <h2
id="use-find-instead-of-ls-to-better-handle-non-alphanumeric-filenames">Use
<code>find</code> instead of <code>ls</code> to better handle
non-alphanumeric filenames.</h2>
<h3 id="problematic-code">Problematic code:</h3>
<div class="sourceCode" id="cb1"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb1-1"><a href="SC2012.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ls</span> <span class="at">-l</span> <span class="kw">|</span> <span class="fu">grep</span> <span class="st">&quot; </span><span class="va">$USER</span><span class="st"> &quot;</span> <span class="kw">|</span> <span class="fu">grep</span> <span class="st">&#39;\.txt$&#39;</span></span></code></pre></div>
<div class="sourceCode" id="cb2"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb2-1"><a href="SC2012.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="va">NUMGZ</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="fu">ls</span> <span class="at">-l</span> <span class="pp">*</span>.gz <span class="kw">|</span> <span class="fu">wc</span> <span class="at">-l</span><span class="va">)</span><span class="st">&quot;</span></span></code></pre></div>
<h3 id="correct-code">Correct code:</h3>
<div class="sourceCode" id="cb3"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb3-1"><a href="SC2012.html#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">find</span> ./<span class="pp">*</span>.txt <span class="at">-user</span> <span class="st">&quot;</span><span class="va">$USER</span><span class="st">&quot;</span>  <span class="co"># Using the names of the files</span></span></code></pre></div>
<div class="sourceCode" id="cb4"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb4-1"><a href="SC2012.html#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="va">gz_files</span><span class="op">=</span><span class="va">(</span><span class="pp">*</span>.gz<span class="va">)</span></span>
<span id="cb4-2"><a href="SC2012.html#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="va">numgz</span><span class="op">=</span><span class="va">${</span><span class="op">#</span><span class="va">gz_files</span><span class="op">[@]</span><span class="va">}</span> <span class="co"># Sometimes, you just need a count</span></span></code></pre></div>
<h3 id="rationale">Rationale:</h3>
<p><code>ls</code> is only intended for human consumption: it has a
loose, non-standard format and may "clean up" filenames to make output
easier to read.</p>
<p>Here's an example:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb5-1"><a href="SC2012.html#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> ls <span class="at">-l</span></span>
<span id="cb5-2"><a href="SC2012.html#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ex">total</span> 0</span>
<span id="cb5-3"><a href="SC2012.html#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="ex">-rw-r-----</span> 1 me me 0 Feb  5 20:11 foo<span class="pp">?</span>bar</span>
<span id="cb5-4"><a href="SC2012.html#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="ex">-rw-r-----</span> 1 me me 0 Feb  5  2011 foo<span class="pp">?</span>bar</span>
<span id="cb5-5"><a href="SC2012.html#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="ex">-rw-r-----</span> 1 me me 0 Feb  5 20:11 foo<span class="pp">?</span>bar</span></code></pre></div>
<p>It shows three seemingly identical filenames, and did you spot the
time format change? How it formats and what it redacts can differ
between locale settings, <code>ls</code> version, and whether output is
a tty.</p>
<h3 id="tips-for-replacing-ls-with-find">Tips for replacing
<code>ls</code> with <code>find</code>:</h3>
<h4 id="just-the-filenames-maam">Just the filenames, ma'am</h4>
<p>(Note that <code>-maxdepth</code> is not POSIX, but can be simulated
by having the expression call <code>-prune</code> on all directories it
finds, e.g. <code>find ./* -prune -print</code>)</p>
<p><code>ls</code> can usually be replaced by <code>find</code> if it's
just the filenames, or a count of them, that you're after. Note that if
you are using <code>ls</code> to get at the contents of a directory, a
straight substitution of <code>find</code> may not yield the same
results as <code>ls</code>. Here is an example:</p>
<pre><code>$ ls -c1 .snapshot
rnapdev1-svm_4_05am_6every4hours.2019-04-01_1605
rnapdev1-svm_4_05am_6every4hours.2019-04-01_2005
rnapdev1-svm_4_05am_6every4hours.2019-04-02_0005
rnapdev1-svm_4_05am_6every4hours.2019-04-02_0405
rnapdev1-svm_4_05am_6every4hours.2019-04-02_0805
rnapdev1-svm_4_05am_6every4hours.2019-04-02_1205
snapmirror.1501b4aa-3f82-11e8-9c31-00a098cef13d_2147868328.2019-04-01_190000</code></pre>
<p>versus</p>
<pre><code>$ find .snapshot -maxdepth 1
.snapshot
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0005
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0405
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0805
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_1605
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_2005
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_1205
.snapshot/snapmirror.1501b4aa-3f82-11e8-9c31-00a098cef13d_2147868328.2019-04-01_190000</code></pre>
<p>You can see two differences here. The first is that the
<code>find</code> output has the full paths to the found files, relative
to the current working directory from which <code>find</code> was run
whereas <code>ls</code> only has the filenames. You may have to adjust
your code to not add the directory to the filenames as you process them
when moving from <code>ls</code> to <code>find</code>, or (with GNU
find) use <code>-printf '%P\n'</code> to print just the filename.</p>
<p>The second difference in the two outputs is that the
<code>find</code> command includes the searched directory as an entry.
This can be eliminated by also using <code>-mindepth 1</code> to skip
printing the root path, or using a negative name option for the searched
directory:</p>
<pre><code>$ find .snapshot -maxdepth 1 ! -name .snapshot
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0005
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0405
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0805
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_1605
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_2005
.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_1205
.snapshot/snapmirror.1501b4aa-3f82-11e8-9c31-00a098cef13d_2147868328.2019-04-01_190000</code></pre>
<p><strong>Note:</strong> If the directory argument to <code>find</code>
is a fully expressed path (<code>/home/somedir/.snapshot</code>), then
you should use <code>basename</code> on the <code>-name</code>
filter:</p>
<pre><code>$ theDir=&quot;$HOME/.snapshot&quot;
$ find &quot;$theDir&quot; -maxdepth 1 ! -name &quot;$(basename $theDir)&quot;
/home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0005
/home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0405
/home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_0805
/home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_1605
/home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-01_2005
/home/matt/.snapshot/rnapdev1-svm_4_05am_6every4hours.2019-04-02_1205
/home/matt/.snapshot/snapmirror.1501b4aa-3f82-11e8-9c31-00a098cef13d_2147868328.2019-04-01_190000</code></pre>
<h4 id="all-the-other-info">All the other info</h4>
<p>If trying to parse out any other fields, first see whether
<code>stat</code> (GNU, OS X, FreeBSD) or <code>find -printf</code>
(GNU) can give you the data you want directly. When trying to determine
file size, try: <code>wc -c</code>. This is more portable as
<code>wc</code> is a mandatory unix command, unlike <code>stat</code>
and <code>find -printf</code>. It may be slower as unoptimized
<code>wc -c</code> may read the entire file rather than just checking
its properties. On some systems, <code>wc -c</code> adds whitespace to
the file size which can be trimmed by double expansion:
<code>$(( $(wc -c &lt; "filename") ))</code></p>
<h3 id="exceptions">Exceptions:</h3>
<p>If the information is intended for the user and not for processing
(<code>ls -l ~/dir | nl; echo "Ok to delete these files?"</code>) you
can ignore this error with a <a href="directive.html">directive</a>.</p>
    <hr />
    <p style='font-size: 80%'><a href="../index.html">ShellCheck</a> is a static analysis tool for shell scripts. This page is part of its documentation.</p>
  </body>
</html>


